#include "drv_at.h"
#include "drv_uart.h"
#include "drv_time.h"

static at_handle_t at_handle = {
    .is_init = 0,
};

static int32_t drv_at_uart_send(uint8_t *p_data, uint16_t len, uint32_t timeout)
{
	drv_uart3_send_buffer(p_data, len);
	return len;
}


uint8_t g_rsp_buf[AIOT_AT_RSP_RB_SIZE_DEFAULT] = {0};
static int32_t drv_ringbuf_rsp_bf_init(ringbuf_t *rbuf, uint32_t size)
{
    if (rbuf->buf) {
        return STATE_AT_ALREADY_INITED;
    }
    memset(rbuf, 0, sizeof(ringbuf_t));

    rbuf->buf = g_rsp_buf;
    rbuf->head = rbuf->buf;
    rbuf->tail = rbuf->buf;
    rbuf->end = rbuf->buf + size;
    rbuf->size = size;
    rbuf->mutex = xSemaphoreCreateMutex();

    return STATE_SUCCESS;
}

static int32_t drv_ringbuf_get_occupy(ringbuf_t *rbuf)
{
    uint32_t used = 0;
	
    if (rbuf->tail >= rbuf->head) {
        used = rbuf->tail - rbuf->head;
    } else {
        used = rbuf->tail - rbuf->buf + rbuf->end - rbuf->head;
    }

    return used;
}

static int32_t __drv_ringbuf_write(ringbuf_t *rbuf, const uint8_t *data, uint32_t len)
{
	xSemaphoreTakeFromISR(rbuf->mutex, NULL);
    if (len > (rbuf->size - drv_ringbuf_get_occupy(rbuf))) {
		xSemaphoreGiveFromISR(rbuf->mutex, NULL);
        return STATE_AT_RINGBUF_OVERFLOW;
    }

    if (rbuf->tail + len >= rbuf->end) {
        uint32_t remain_len = rbuf->end - rbuf->tail;
        memcpy(rbuf->tail, data, remain_len);
        memcpy(rbuf->buf, data + remain_len, len - remain_len);
        rbuf->tail = rbuf->buf + len - remain_len;
    } else {
        memcpy(rbuf->tail, data, len);
        rbuf->tail += len;
    }
	xSemaphoreGiveFromISR(rbuf->mutex, NULL);
	
    return len;
}

int32_t drv_ringbuf_write(const uint8_t *data, uint32_t len)
{
	return __drv_ringbuf_write(&at_handle.rsp_rb, data, len);
}

// todo ringbuf mutex
static int32_t drv_ringbuf_read(ringbuf_t *rbuf, uint8_t *data, uint32_t len)
{
	xSemaphoreTake(rbuf->mutex, portMAX_DELAY );
    int32_t used = drv_ringbuf_get_occupy(rbuf);
    if (len > used) {
		xSemaphoreGive(rbuf->mutex);
        return 0;
    }

    if (rbuf->head + len >= rbuf->end) {
        uint32_t remain_len = rbuf->end - rbuf->head;
        memcpy(data, rbuf->head, remain_len);
        memcpy(data + remain_len, rbuf->buf, len - remain_len);
        rbuf->head = rbuf->buf + len - remain_len;
    } else {
        memcpy(data, rbuf->head, len);
        rbuf->head += len;
    }
	xSemaphoreGive(rbuf->mutex);
	
    return len;
}

void drv_ringbuf_deinit(ringbuf_t *rbuf)
{
    if (NULL == rbuf) {
        return;
    }
    memset(rbuf, 0, sizeof(ringbuf_t));
}

static int32_t drv_ringbuf_read_sync_rsp(uint8_t *rsp, uint32_t len, uint32_t timeout)
{
    uint64_t timeout_start = get_timestamp();
    uint32_t rsp_len = 0;
    uint32_t temp = 0;

    /* try to read out all data in ringbuf */
    do {
        delayms(AIOT_AT_RINGBUF_RETRY_INTERVAL);
        rsp_len = drv_ringbuf_get_occupy(&at_handle.rsp_rb);
        if (rsp_len == temp && rsp_len != 0) {
            break;
        }
        temp = rsp_len;
    } while ((get_timestamp() - timeout_start) < timeout);
    int mini_len = len < rsp_len ? len : rsp_len;

    return drv_ringbuf_read(&at_handle.rsp_rb, rsp, mini_len);
}


static int32_t drv_at_uart_tx(const uint8_t *p_data, uint16_t len, uint32_t timeout)
{
    int32_t res = STATE_SUCCESS;

    if (at_handle.uart_tx_func == NULL) {
        return STATE_AT_UART_TX_FUNC_NULL;
    }

	xSemaphoreTake(at_handle.tx_mutex, portMAX_DELAY );
    res = at_handle.uart_tx_func(p_data, len, timeout);
    xSemaphoreGive(at_handle.tx_mutex);
	
    return res;
}


int32_t drv_at_commands_send(const at_cmd_item_t *cmd_list, uint16_t cmd_num)
{
    uint16_t i = 0;
    uint16_t retry_cnt = AIOT_AT_CMD_RETRY_TIME;
    int32_t res = STATE_SUCCESS;

    if (NULL == cmd_list || 0 == cmd_num) {
        return STATE_USER_INPUT_NULL_POINTER;
    }
	if (at_handle.is_init != 1) {
        return STATE_AT_NOT_INITED;
    }

   // printf("core_at_commands_send_sync\r\n");

    for (; i < cmd_num; i++) {
        char rsp[AIOT_AT_RSP_LEN_MAXIMUM] = {0};
        char delay_rsp[AIOT_AT_RSP_LEN_MAXIMUM] = {0};

        /* 发送AT命令 */
        printf("sending cmd: %s\r\n", cmd_list[i].cmd);
        res = drv_at_uart_tx((uint8_t *)cmd_list[i].cmd, cmd_list[i].cmd_len, at_handle.tx_timeout);
        if (res != cmd_list[i].cmd_len) {
            res = STATE_AT_UART_TX_FAILED;
            break;
        }

        int expected_len = cmd_list[i].expected_len == 0 ? sizeof(rsp) : cmd_list[i].expected_len;
       // printf("expected len: %d\r\n", expected_len);

        /* 获取应答 */
        res = drv_ringbuf_read_sync_rsp((uint8_t *)rsp, expected_len, at_handle.rsp_timeout);
        if (res <= 0) {
			  if (--retry_cnt > 0) {
			  	i--;  //for循环++,此次--还是这个命令
                continue;
            } else {
            	res = STATE_AT_UART_RX_FAILED;
                break;
            }
        }
        printf("recved rsp: %s expected %s\r\n", rsp,  cmd_list[i].rsp);

        if (cmd_list[i].delay_rsp != NULL) {
            res = drv_ringbuf_read_sync_rsp((uint8_t *)delay_rsp, sizeof(delay_rsp), at_handle.rsp_timeout);
            printf("recved delay_rsp: %s, expected %s\r\n", delay_rsp,  cmd_list[i].delay_rsp);
        }


        /* call user handle */
        if (cmd_list[i].handler) {
            if ((res = cmd_list[i].handler(rsp, delay_rsp)) != STATE_SUCCESS) {
                break;
            }
        }

        //todo add delay response handler
        if (cmd_list[i].rsp != NULL && strstr(rsp, cmd_list[i].rsp) == NULL) {
            res = STATE_AT_GET_RSP_FAILED;
            break;
        }

        retry_cnt = AIOT_AT_CMD_RETRY_TIME;
        res = STATE_SUCCESS;
    }

    return res;
}


int32_t drv_at_init(void)
{
    int32_t res = STATE_SUCCESS;

    if (at_handle.is_init != 0) {
        return STATE_AT_ALREADY_INITED;
    }

    memset(&at_handle, 0, sizeof(at_handle_t));
    at_handle.tx_timeout = AIOT_AT_TX_TIMEOUT_DEFAULT;
    at_handle.rsp_timeout = AIOT_AT_RX_TIMEOUT_DEFAULT;

    at_handle.tx_mutex = xSemaphoreCreateMutex();
    at_handle.uart_tx_func = drv_at_uart_send;
    drv_ringbuf_rsp_bf_init(&at_handle.rsp_rb, AIOT_AT_RSP_RB_SIZE_DEFAULT);

    at_handle.is_init = 1;
    return res;
}

